package com.yonyou.pmclouds.projectpart.upgrade.rmiimpl;

import com.alibaba.dubbo.config.annotation.Service;
import com.yonyou.pmclouds.ApplicationConsts;
import com.yonyou.pmclouds.basecom.bp.processor.AuditInfoInsertMultiProcessor;
import com.yonyou.pmclouds.basecom.bp.template.MultiInsertBpTemplate;
import com.yonyou.pmclouds.basecom.consts.EnableStatusConst;
import com.yonyou.pmclouds.basecom.exception.BusinessException;
import com.yonyou.pmclouds.projectpart.entity.ProjectPartVO;
import com.yonyou.pmclouds.projectpart.mapper.ProjectpartMapper;
import com.yonyou.pmclouds.projectpart.processor.ProjectpartFillDataProcessor;
import com.yonyou.pmclouds.projectpart.upgrade.service.IProjectPartBusinessOperator;
import com.yonyou.pmclouds.projectpart.upgrade.service.IProjectPartUpgrade;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author th
 * @date 2020/4/17 15:45
 */
@Service(interfaceClass = IProjectPartUpgrade.class, version = ApplicationConsts.APPLICATION_VERSION)
@Slf4j
public class ProjectPartUpgradeImpl implements IProjectPartUpgrade {
    private Map<String, IProjectPartBusinessOperator> businessOperatorMap = new HashMap<>();

    @Autowired
    private ProjectpartFillDataProcessor projectpartFillDataProcessor;
    @Autowired
    private ProjectpartMapper projectpartMapper;

    static ConcurrentHashMap<String,AtomicInteger> map = new ConcurrentHashMap();
    static ConcurrentHashMap<String,AtomicInteger> namemap = new ConcurrentHashMap();
    static AtomicInteger repeatTotal = new AtomicInteger();

    private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10,
            30L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));

    @Override
    public void addBusinessOperatorClass(IProjectPartBusinessOperator operator) {
        if (operator != null) {
            businessOperatorMap.put(operator.getOperateBillType(), operator);
        }
    }

    @Override
    public boolean upgrade() throws BusinessException {
        if (businessOperatorMap.isEmpty()) {
            log.error("没有要升级的业务单据");
            return false;
        }

        for (Map.Entry<String, IProjectPartBusinessOperator> entry : businessOperatorMap.entrySet()) {
            threadPoolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    String billType = entry.getKey();
                    IProjectPartBusinessOperator businessOperator = entry.getValue();
                    try {
                        // 构造工程部位
                        List<ProjectPartVO> insertVOs = businessOperator.constructProjectPartVOsOfBill();
                        if (insertVOs == null || insertVOs.isEmpty())
                            return;

                        log.info(billType+"单据，携带" + insertVOs.size()+"条数据进行插入");
                        // 插入
                        insertVOs = insertBatchForMobile(insertVOs);
                        // 更新业务单据
                        businessOperator.updatePkProjectParts(insertVOs);
                    } catch (BusinessException e) {
                        log.error("单据类型:" + billType + "升级工程部位失败", e);
                    }
                }
            });
        }

        return true;
    }

    @Override
    public List<ProjectPartVO> insertBatchForMobile(List<ProjectPartVO>  projectPartVOs) throws BusinessException {
        if(CollectionUtils.isEmpty(projectPartVOs)){
            return null;
        }
        ConcurrentHashMap<String,Integer> countMap = new ConcurrentHashMap<>();
        log.info("线程【"+Thread.currentThread().getId()+"】携带数据量："+projectPartVOs.size());
        List<ProjectPartVO> result = projectPartVOs.stream().map(vo -> {
            if(StringUtils.isEmpty(vo.getPkProject())){
                return null;
            }
            Integer count = countMap.get(vo.getPkProject()) != null? countMap.get(vo.getPkProject()):0;
            if(StringUtils.isEmpty(vo.getPkTenant())
                    || StringUtils.isEmpty(vo.getPkProject())
                    || StringUtils.isEmpty(vo.getSname())){
                return null;
            }
            if(count < 999){
                countMap.put(vo.getPkProject(),count+1);
//                log.info("线程："+Thread.currentThread().getId()+"，项目：【" + vo.getPkProject()+" 】对应工程部位数量为："+countMap.get(vo.getPkProject()));
                return vo;
            }else{
                return null;
            }
        }).filter(Objects::nonNull).collect(Collectors.toList());
        log.info("线程【"+Thread.currentThread().getId()+"】过滤后数据量："+projectPartVOs.size());
        /**
         * 历史数据升级，将数据挂载到一级。目前每一级最大容量为999，考虑到实际生产环境中没有太多数据，
         * 且开发环境测试数据太多，因此将内码超过999的数据丢掉
         */
        List<ProjectPartVO> data = new ArrayList<>();
        for(ProjectPartVO vo : projectPartVOs){
            if(StringUtils.isEmpty(vo.getPkProject())){
                continue;
            }
            if(namemap.get(vo.getPkProject()+","+vo.getSname()) != null){
                repeatTotal.incrementAndGet();
                continue;
            }else{
                namemap.putIfAbsent(vo.getPkProject()+","+vo.getSname(),new AtomicInteger(0));
            }
            AtomicInteger resource = map.get(vo.getPkProject());
            if(resource == null){
                String innerCode = projectpartFillDataProcessor.getProjectNextInnerCode(vo);
                if(Integer.valueOf("999") - Integer.valueOf(innerCode) < 0){
                    continue ;
                }
                map.putIfAbsent(vo.getPkProject(),new AtomicInteger(Integer.valueOf(innerCode)));
                resource = map.get(vo.getPkProject());
            }
            Integer scode = map.get(vo.getPkProject()).incrementAndGet();
            if(scode == null){
                throw new BusinessException("生成工程部位编码失败");
            }
            if(Integer.valueOf("999") - Integer.valueOf(scode) < 0){
                continue ;
            }
//            包装scode值
            String scodeStr =  fillUpgradeScode( scode);
            vo.setScode(scodeStr);
            //填充内码和scode
            vo.setInnerCode(scodeStr);
            vo.setScode("Y"+scodeStr);
            vo.setCreator("upgrader");
//                log.info("线程："+Thread.currentThread().getId()+"，项目:"+vo.getPkProject()+"，生成的编码："+scode);
            vo.setEnableStatus(EnableStatusConst.ENABLE);
            data.add(vo);
        }

        if(data.size() > 0){
            MultiInsertBpTemplate<ProjectPartVO> insertBpTemplate = new MultiInsertBpTemplate<>(projectpartMapper);
            insertBpTemplate.addBeforeProcessor(new AuditInfoInsertMultiProcessor<ProjectPartVO>());
            insertBpTemplate.insert(data.toArray(new ProjectPartVO[]{}));
//            log.info("线程【"+Thread.currentThread().getId()+"】命中重复数据量："+repeatTotal.intValue());
            log.info("线程【"+Thread.currentThread().getId()+"】，生成工位部位数据量："+data.size());
        }
        //数据入库
        return data;
    }

    private String fillUpgradeScode(Integer scode){
        String numTemp = scode.toString();
        if(scode > 0){
            String prefix = "";
            for(int i = 0; i< 3 - numTemp.length();i++){
                prefix += "0";
            }
            return prefix+numTemp;
        }
        return  numTemp;
    }

}
